<template>
  <div class="con">
  <div class="h-screen">
      <div class="top1">
        <div class="t_title">YiBaoLLM</div>
        <div class="btn_1" @click="clickConfig()">
          设置
        </div>
      </div>

      <!-- Chat list area -->
      <div class="talk1" ref="chatListDom">
        <!-- Message group -->
        <div class="chat-ai" v-for="item of messageList.filter((v) => v.role !== 'system')">
          <div class="fuzhi">
            <div class="font-bold">{{ roleAlias[item.role] }}：</div>
            <Copy class="input_big" :content="item.content" />
          </div>
          <div>
            <div class="neirong" v-if="item.content" v-html="md.render(item.content)"></div>
            <!-- Waiting for answer loading effect -->
            <Loding v-else />
          </div>
        </div>
      </div>

      <!-- Buttons area -->
    
      <div style="margin-top: 10px;">
        <div style="margin-bottom: 10px;">
          <!-- Buttons using Element-UI-Plus -->
          <el-button>问题分析</el-button>
          <!-- Repeat for other buttons... -->
          <el-button>知识问答</el-button>
          <!-- Clear button -->
          <el-button @click="clear()">清空</el-button>
        </div>
      </div>
  
      <!-- Fixed input and send button area -->
      <div class="fixed">
        <div>
          <input
            class="input11"
            :type="isConfig ? 'password' : 'text'"
            :placeholder="isConfig ? 'sk-xxxxxxxxxx' : '请输入'"
            v-model="messageContent"
            @keydown.enter="isTalking || sendOrSave()"
          />
          <el-button :disabled="isTalking" @click="sendOrSave()" style="margin-left: 20px;">
            {{ isConfig ? "保存" : "发送" }}
          </el-button>
        </div>
      </div>

  </div>
</div>
</template>

<script setup lang="ts">
import type { ChatMessage } from "@/types";
import { ref, watch, nextTick, onMounted } from "vue";
import { chat } from "@/libs/auditllm";
import cryptoJS from "crypto-js";
import Loding from "@/components/Loding.vue";
import Copy from "@/components/Copy.vue";
import { md } from "@/libs/markdown";

let apiKey = "";
let isConfig = ref(false);
let isTalking = ref(false);
let messageContent = ref("");
let smallModelProbability = ref("")
const chatListDom = ref<HTMLDivElement>();
const decoder = new TextDecoder("utf-8");
const roleAlias = { user: "问题", assistant: "YB", system: "System" };
const messageList = ref<ChatMessage[]>([
  {
    role: "system",
    content: "你是大模型 AuditLLm，尽可能简洁地回答。",
  },
  {
    role: "assistant",
    content: `你好，我是大模型，我可以提供一些常用服务和信息，例如：
1. 医保等方面的内容。
2. 医保相关问题解答。
请告诉我你需要哪方面的帮助，我会根据你的需求给你提供相应的信息和建议。`,
  },
]);

onMounted(() => {
  if (getAPIKey()) {
    switchConfigStatus();
  }
});

const sendChatMessage = async (content: string = messageContent.value) => {
  try {
    isTalking.value = true;
    if (messageList.value.length === 2) {
      messageList.value.pop();
    }
    messageList.value.push({ role: "user", content });
    let a=messageContent.value
    clearMessageContent();
    messageList.value.push({ role: "assistant", content: "" });
    const res = await  chat(messageList.value, a)
    // const { body, status } = await chat(messageList.value, getAPIKey());
    
   
    //const res = await  chat(messageList.value, messageContent.value)

    if (res) {
      // 进行打字效果
      type(res);
    }
  } catch (error: any) {
    appendLastMessageContent(error);
  } finally {
    isTalking.value = false;
  }
};

// 通过分割字符串打字效果
const type = async (content: string) => {
  let curIndex = 0
  const probabilityTimer = setInterval(() => {
    //先清空
    smallModelProbability.value = ""
    smallModelProbability.value = content[curIndex];
    if (curIndex <  content.length) {
      curIndex++;
      appendLastMessageContent(smallModelProbability.value)
    }
    else {
      clearInterval(probabilityTimer);
    }
  }, 20)    //20是时间间隔
}

const readStream = async (
  reader: ReadableStreamDefaultReader<Uint8Array>,
  status: number
) => {
  let partialLine = "";

  while (true) {
    // eslint-disable-next-line no-await-in-loop
    const { value, done } = await reader.read();
    if (done) break;

    const decodedText = decoder.decode(value, { stream: true });

    if (status !== 200) {
      const json = JSON.parse(decodedText); // start with "data: "
      const content = json.error.message ?? decodedText;
      appendLastMessageContent(content);
      return;
    }

    const chunk = partialLine + decodedText;
    const newLines = chunk.split(/\r?\n/);

    partialLine = newLines.pop() ?? "";

    for (const line of newLines) {
      if (line.length === 0) continue; // ignore empty message
      if (line.startsWith(":")) continue; // ignore sse comment message
      if (line === "data: [DONE]") return; //

      const json = JSON.parse(line.substring(6)); // start with "data: "
      const content =
        status === 200
          ? json.choices[0].delta.content ?? ""
          : json.error.message;
      appendLastMessageContent(content);
    }
  }
};

const appendLastMessageContent = (content: string) =>
  (messageList.value[messageList.value.length - 1].content += content);

const sendOrSave = () => {
  if (!messageContent.value.length) return;
  if (isConfig.value) {
    if (saveAPIKey(messageContent.value.trim())) {
      switchConfigStatus();
    }
    clearMessageContent();
  } else {
    sendChatMessage();
  }
};

const clear = () => {
  let i: number
    for (i = 0; i <  messageList.value.length; i++){
      messageList.value.pop();
    }

};

const clickConfig = () => {
  if (!isConfig.value) {
    messageContent.value = getAPIKey();
  } else {
    clearMessageContent();
  }
  switchConfigStatus();
};

const getSecretKey = () => "lianginx";

const saveAPIKey = (apiKey: string) => {
  if (apiKey.slice(0, 3) !== "sk-" || apiKey.length !== 51) {
    alert("API Key 错误，请检查后重新输入！");
    return false;
  }
  const aesAPIKey = cryptoJS.AES.encrypt(apiKey, getSecretKey()).toString();
  localStorage.setItem("apiKey", aesAPIKey);
  return true;
};

const getAPIKey = () => {
  if (apiKey) return apiKey;
  const aesAPIKey = localStorage.getItem("apiKey") ?? "";
  apiKey = cryptoJS.AES.decrypt(aesAPIKey, getSecretKey()).toString(
    cryptoJS.enc.Utf8
  );
  return apiKey;
};

const switchConfigStatus = () => (isConfig.value = !isConfig.value);

const clearMessageContent = () => (messageContent.value = "");

const scrollToBottom = () => {
  if (!chatListDom.value) return;
  scrollTo(0, chatListDom.value.scrollHeight);
};

watch(messageList.value, () => nextTick(() => scrollToBottom()));
</script>

<style>

.con{
  display: flex;
  flex-direction: row;
  height: 590px;
  width: 100%;
  max-width: 1100px; /* 设置最大宽度 */
  margin: 50px auto; /* 居中显示 */
  background-color: var(--background-color);
  padding: 20px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
}
.btn {
  margin: 0; /* Remove default button margin */
}

.input {
  flex-grow: 1; /* Expand input to fill available space */
}

.top1 {
  display: flex; /* 使用flex布局 */
  align-items: center; /* 垂直居中对齐 */
  justify-content: space-between; /* 两端对齐，按钮在右侧 */
  width: 1100px;
  height: 50px;
}

/* 标题样式 */
.t_title {
  font-size: 24px; /* 设置较大的字体大小 */
  font-weight: bold; /* 加粗字体 */
  margin-right: 1px; /* 与其他元素保持一定距离 */
}

/* 副标题样式 */
.t_title2 {
  font-size: 18px; /* 设置较小的字体大小 */
  color: #666; /* 设置字体颜色，例如灰色 */
}

/* 按钮样式 */
.btn_1 {
  font-size: 16px; /* 设置默认字体大小 */
  padding: 8px 20px; /* 添加一些内边距 */
  background-color: #007bff; /* 设置按钮背景颜色 */
  color: white; /* 设置按钮文字颜色为白色 */
  border: none; /* 移除按钮边框 */
  border-radius: 5px; /* 圆角按钮 */
  cursor: pointer; /* 鼠标悬停时变为手指形状 */
}

/* 按钮悬浮效果 */
.btn_1:hover {
  background-color: #0056b3; /* 悬浮时背景颜色变深 */
}

.talk1 {
  height: 460px; /* 设置固定高度 */
  width: 1100px; /* 设置固定宽度 */
  overflow-y: auto; /* 如果内容超出高度，显示垂直滚动条 */
  overflow-x: hidden; /* 隐藏水平滚动条，除非内容宽度超出宽度 */
  position: relative; /* 为子元素的绝对或固定定位提供参考 */
  background-color: #ffffff;
}

/* .chat-ai 容器的基本样式 */
.chat-ai {
  position: relative; /* 设置为相对定位，为子元素的绝对定位提供参考 */
}

/* .neirong 容器的基本样式 */
.neirong {
  position: relative; /* 设置为相对定位，为子元素的绝对定位提供参考 */
  display: inline-block; /* 使元素表现为行内块级元素 */
  cursor: pointer; /* 鼠标悬停时变为手指形状 */
}

/* .input_big 隐藏的基本样式 */
.input_big {
  display: none; /* 默认不显示 */
  position: absolute; /* 绝对定位 */
  top: 0; /* 与 .neirong 顶部对齐 */
  right: 100%; /* 放置在 .neirong 右侧外面 */
  margin-right: 10px; /* 与 .chat-ai 容器的右侧保持一定距离 */
  background-color: #fff; /* 输入框的背景颜色 */
  padding: 5px; /* 输入框的内边距 */
  border: 1px solid #262525; /* 输入框的边框 */
  border-radius: 5px; /* 输入框的圆角 */
  z-index: 1; /* 确保覆盖在其他元素之上 */
}

.neirong:hover .input_big {
  display: block; /* 显示元素 */
}

/* 定义一个CSS类来覆盖默认的输入框样式 */
.input11 {
  width: 960px; /* 使输入框宽度为100%，占满容器 */
  padding: 5.5px 10px; /* 增加内边距，让输入框看起来更宽敞 */
  font-size: 16px; /* 增加字体大小，提高可读性 */
  border: 1px solid #ccc; /* 添加边框，使输入框更显眼 */
  border-radius: 5px; /* 圆角边框，看起来更现代 */
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); /* 添加阴影效果，增加层次感 */
  transition: border-color 0.3s, box-shadow 0.3s; /* 过渡效果，使边框和阴影的变化更平滑 */
}

/* 当输入框获得焦点时的样式 */
.input11:focus {
  outline: none; /* 移除默认的焦点轮廓 */
  border-color: #007bff; /* 改变边框颜色 */
  box-shadow: 0 0 8px rgba(0, 123, 255, 0.3); /* 增加更明显的阴影效果 */
}

/* 可以添加一些伪元素或状态相关的样式来进一步美化输入框 */
.input11::placeholder {
  color: #999; /* 占位符文本颜色 */
}

/* 当输入框处于禁用状态时的样式 */
.input11:disabled {
  background-color: #f8f9fa; /* 禁用时的背景颜色 */
  color: #6c757d; /* 禁用时的文本颜色 */
  cursor: not-allowed; /* 改变光标样式 */
}
</style>